iOS Crash 一

初衷

当前客户端中使用的crash收集工具为第三方提供,用到的第三方工具有

  • fabric
  • bugly

第三方工具在使用的过程中,存在的问题:

  • 内聚的功能过多。有的功能我们并不会用到,成为了负担
  • 定制化场景少。对于不同的app,希望错不同的维度来查询对应的结果
  • SDK size 较大

针对上面的这些问题,打算出一套自己的crash工具,工具本省应该具备的功能:

  • 功能单一性
  • 健壮性
  • 定制化产出

app发生crash之后如何定位

当手机中的应用发生奔溃了之后,都会在系统日志中生成一条crash日志,这里为了测试,会新建一个应用,模拟crash。比如一个数组越界的crash

NSMutableArray *array = [NSMutableArray array];
[array addObject:nil];

工程创建好了之后,可以先在xcode中编译一下,出发这里的demo,会发现奔溃现象,奔溃现场如下:

从奔溃的堆栈中我们可以发现如下信息:

  • reson
  • 出错堆栈
  • 函数调用的过程

当前Debug中看到的crash可读性很好,方便定位crash出错的位置。这是因为XCode默认帮我们做了很多事情,从crash的产生到符号化,都在无感知的状态下帮我们做好了。更有甚者,如果打开了全局的断点调试功能,在奔溃的同时还会定位到出错的代码行。这样的便利这是在xcode IDE环境中,现实的使用场景中,获取到的crash,基本上是不可读的。为了模拟真实的用户场景,通过如下方式模拟:

  1. archive的方式导出当前的release包资源
  2. 载通过itools之类的工具安装
  3. 点击可以crash的地方,产生几条crash记录

正常情况下,我们启动xcode,通过 Window -> Devices and Simulators -> 选中当前crash手机(这里需要usb连接) -> View Devices Logs。等待xcode scaning完成,应该就会看到crash文件,保险起见,最好check一下时间。但是也有看不到情况,例如我的xcode就没有执行scaning的操作,导致没有同步到crash数据。

如果遇到了同样的情况,请走下面的操作:

  1. USB连接电脑
  2. 打开iTunes同步手机到当前的电脑上
  3. cd 到 ~/Library/Logs/CrashReporter/MobileDevice/** iphone
  4. ls

就可以看到对应的crash文件了,如下

看一下同样的报错在真实环境中是什么的:

Incident Identifier: 056AE3FF-0053-4B5F-8C42-5CFD2F4CE9F4
CrashReporter Key:   3c6d6b39fd3e330ce44828e97ab49661480cbb94
Hardware Model:      iPhone10,3
Process:             TestCrash [652]
Path:                /private/var/containers/Bundle/Application/538B29B9-CED1-4969-913B-CB9344F70E94/TestCrash.app/TestCrash
Identifier:          com.lianjia.TestCrash
Version:             1 (1.0)
Code Type:           ARM-64 (Native)
Role:                Foreground
Parent Process:      launchd [1]
Coalition:           com.lianjia.TestCrash [682]


Date/Time:           2018-08-10 17:43:31.2405 +0800
Launch Time:         2018-08-10 17:43:27.4744 +0800
OS Version:          iPhone OS 11.4.1 (15G77)
Baseband Version:    1.93.00
Report Version:      104

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Triggered by Thread:  0

Application Specific Information:
abort() called

Filtered syslog:
None found

Last Exception Backtrace:
(0x18308ad8c 0x1822445ec 0x183023750 0x182f5705c 0x1046d105c 0x18cdf964c 0x18cf1a870 0x18cdff700 0x18cf351a8 0x18ce7c9e0 0x18ce71890 0x18ce701d0 0x18d651d1c 0x18d6542c8 0x18d64d368 0x183033404 0x183032c2c 0x18303079c 0x182f50da8 0x184f36020 0x18cf70758 0x1046d1134 0x1829e1fc0)

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libsystem_kernel.dylib            0x0000000182b112e0 0x182aef000 + 140000
1   libsystem_pthread.dylib           0x0000000182cb66a8 0x182caf000 + 30376
2   libsystem_c.dylib                 0x0000000182a7fd0c 0x182a1d000 + 404748
3   libc++abi.dylib                   0x000000018221b2c8 0x18221a000 + 4808
4   libc++abi.dylib                   0x000000018221b470 0x18221a000 + 5232
5   libobjc.A.dylib                   0x00000001822448d4 0x18223c000 + 35028
6   libc++abi.dylib                   0x000000018223537c 0x18221a000 + 111484
7   libc++abi.dylib                   0x0000000182234f78 0x18221a000 + 110456
8   libobjc.A.dylib                   0x00000001822447ac 0x18223c000 + 34732
9   CoreFoundation                    0x0000000182f50e18 0x182f45000 + 48664
10  GraphicsServices                  0x0000000184f36020 0x184f2b000 + 45088
11  UIKit                             0x000000018cf70758 0x18cc53000 + 3266392
12  TestCrash                         0x00000001046d1134 0x1046c8000 + 37172
13  libdyld.dylib                     0x00000001829e1fc0 0x1829e1000 + 4032

Thread 1:
0   libsystem_pthread.dylib           0x0000000182cafb04 0x182caf000 + 2820

Thread 2:
0   libsystem_pthread.dylib           0x0000000182cafb04 0x182caf000 + 2820

Thread 3:
0   libsystem_pthread.dylib           0x0000000182cafb04 0x182caf000 + 2820

Thread 4:
0   libsystem_kernel.dylib            0x0000000182b11d78 0x182aef000 + 142712
1   libsystem_pthread.dylib           0x0000000182cb00a0 0x182caf000 + 4256
2   libsystem_pthread.dylib           0x0000000182cafb08 0x182caf000 + 2824

Thread 5 name:  com.apple.uikit.eventfetch-thread
Thread 5:
0   libsystem_kernel.dylib            0x0000000182aefde8 0x182aef000 + 3560
1   libsystem_kernel.dylib            0x0000000182aefc60 0x182aef000 + 3168
2   CoreFoundation                    0x0000000183032e40 0x182f45000 + 974400
3   CoreFoundation                    0x0000000183030908 0x182f45000 + 964872
4   CoreFoundation                    0x0000000182f50da8 0x182f45000 + 48552
5   Foundation                        0x00000001839c5674 0x1839bd000 + 34420
6   Foundation                        0x00000001839c551c 0x1839bd000 + 34076
7   UIKit                             0x000000018cc55768 0x18cc53000 + 10088
8   Foundation                        0x0000000183ad5efc 0x1839bd000 + 1150716
9   libsystem_pthread.dylib           0x0000000182cb1220 0x182caf000 + 8736
10  libsystem_pthread.dylib           0x0000000182cb1110 0x182caf000 + 8464
11  libsystem_pthread.dylib           0x0000000182cafb10 0x182caf000 + 2832

Thread 6:
0   libsystem_pthread.dylib           0x0000000182cafb04 0x182caf000 + 2820

Thread 0 crashed with ARM Thread State (64-bit):
    x0: 0x0000000000000000   x1: 0x0000000000000000   x2: 0x0000000000000000   x3: 0x00000001cc0eedb7
    x4: 0x000000018223aabd   x5: 0x000000016b7373f0   x6: 0x000000000000006e   x7: 0x000000000000030b
    x8: 0x0000000008000000   x9: 0x0000000004000000  x10: 0x0000000182cb2110  x11: 0x0000000000000003
   x12: 0xffffffffffffffff  x13: 0x0000000000000001  x14: 0x0000000000000000  x15: 0x0000000000000010
   x16: 0x0000000000000148  x17: 0x0000000000000300  x18: 0x0000000000000000  x19: 0x0000000000000006
   x20: 0x00000001b55dbb40  x21: 0x000000016b7373f0  x22: 0x0000000000000303  x23: 0x00000001b55dbc20
   x24: 0x0000000000000001  x25: 0x00000001c00199f0  x26: 0x0000000000000000  x27: 0x0000000000000001
   x28: 0x000000016b737b38   fp: 0x000000016b737350   lr: 0x0000000182cb66a8
    sp: 0x000000016b737320   pc: 0x0000000182b112e0 cpsr: 0x00000000

Binary Images:
0x1046c8000 - 0x1046d3fff TestCrash arm64  <1033c07aba8b3895bd20eb85e761bb49> /var/containers/Bundle/Application/538B29B9-CED1-4969-913B-CB9344F70E94/TestCrash.app/TestCrash
...
0x1ab910000 - 0x1ab940fff libclosured.dylib arm64  <e61ffac51cae3e1fb9eb6a6e2801777b> /usr/lib/closure/libclosured.dylib

EOF

这里是未符号化的crash文件。符号化crash文件还需要对应的符号文件,这个符号文件可以在之前的archive中找到,路径为:Xcode -> Window -> Organizer -> Archive -> 选中App -> 选中其中的最顶部的archive -> show in finder -> 显示包内容 -> dsYMs文件下 -> **.app.dSYM

手动符号化文件

为了之后的操作方便,可以将crash文件和dYSM文件放在同一个文件夹下。首先需要确定当前的crash文件和dSYM文件是否是一一对应的,校验的规则为比对uuid是否相同(这里的UUID是build级别的,每次build产生的UUID都有可能不相同),使用如下命令获取crash uuid

grep "appName arm" *.crash
结果:
TestCrash-2018-08-10-174331.crash:0x1046c8000 - 0x1046d3fff TestCrash arm64  
<1033c07aba8b3895bd20eb85e761bb49> /var/containers/Bundle/Application/538B29B9-CED1-4969-913B-CB9344F70E94/TestCrash.app/TestCrash

使用如下命令查看dYSM文件的uuid

dwarfdump --uuid TestCrash.app.dSYM/Contents/Resources/DWARF/TestCrash
结果:UUID: 1033C07A-BA8B-3895-BD20-EB85E761BB49 (arm64) TestCrash.app.dSYM/Contents/Resources/DWARF/TestCrash

确定了上面的事情之后,使用符号化工具 symbolicatecrash,使用之前需要先导出这个工具,使用如下命令:

export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer

运行了之后看一下本地文件下有没有symbolicatecrash,如果不存在,可以使用如下命令查看symbolicatecrash所在位置:

find /Applications/Xcode.app/ -name symbolicatecrash
结果可能是多个
/Applications/Xcode.app//Contents/Developer/Platforms/WatchSimulator.platform/Developer/Library/PrivateFrameworks/DVTFoundation.framework/symbolicatecrash
/Applications/Xcode.app//Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/Library/PrivateFrameworks/DVTFoundation.framework/symbolicatecrash
/Applications/Xcode.app//Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/DVTFoundation.framework/symbolicatecrash
/Applications/Xcode.app//Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash
选择SharedFramework中的文件 copy 到本地
cp /Applications/Xcode.app//Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash ./

当前文件已经存在直接运行

./symbolicatecrash TestCrash-2018-08-10-174331.crash TestCrash.app.dSYM > a.log

符号化之后的文件如下

...

Last Exception Backtrace:
0   CoreFoundation                    0x18308ad8c __exceptionPreprocess + 228
1   libobjc.A.dylib                   0x1822445ec objc_exception_throw + 55
2   CoreFoundation                    0x183023750 _CFThrowFormattedException + 111
3   CoreFoundation                    0x182f5705c -[__NSArrayM insertObject:atIndex:] + 1411
4   TestCrash                         0x1046d105c -[ViewController function9] + 36956 (ViewController.m:88)
5   UIKit                             0x18cdf964c -[UIApplication sendAction:to:from:forEvent:] + 95
6   UIKit                             0x18cf1a870 -[UIControl sendAction:to:forEvent:] + 79
7   UIKit                             0x18cdff700 -[UIControl _sendActionsForEvents:withEvent:] + 439
8   UIKit                             0x18cf351a8 -[UIControl touchesEnded:withEvent:] + 571
9   UIKit                             0x18ce7c9e0 -[UIWindow _sendTouchesForEvent:] + 2427
10  UIKit                             0x18ce71890 -[UIWindow sendEvent:] + 3159
11  UIKit                             0x18ce701d0 -[UIApplication sendEvent:] + 339
12  UIKit                             0x18d651d1c __dispatchPreprocessedEventFromEventQueue + 2339
13  UIKit                             0x18d6542c8 __handleEventQueueInternal + 4743
14  UIKit                             0x18d64d368 __handleHIDEventFetcherDrain + 151
15  CoreFoundation                    0x183033404 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 23
16  CoreFoundation                    0x183032c2c __CFRunLoopDoSources0 + 275
17  CoreFoundation                    0x18303079c __CFRunLoopRun + 1203
18  CoreFoundation                    0x182f50da8 CFRunLoopRunSpecific + 551
19  GraphicsServices                  0x184f36020 GSEventRunModal + 99
20  UIKit                             0x18cf70758 UIApplicationMain + 235
21  TestCrash                         0x1046d1134 main + 37172 (main.m:14)
22  libdyld.dylib                     0x1829e1fc0 start + 3

...

借助工具完成符号化

从上面手动解析的工程中,我们可以看到,需要的数据和工具有

  • dSYM文件
  • symbolicatecrash工具

并且这两个工具都在xcode中,前提条件是这个ipa包是通过你的xcode archive出去的,当前的机器上保留了dSYM文件。之后只需要将crash文件拖到xcode crash中就会自动解析了。效果如下:

Xcode 统计的Crash

app 上传到APP store之后,使用过程中产生的crash,同时当前的设备打开了日志分析的功能,crash日志就会被Apple统计到。通过步骤 Xcode -> Window -> Organizer -> 选中Crash -> 选中对应的app 可以看到对应的crash,符号化的过程,可以在本地,也可以在apple服务端,取决于,上传app 二进制包的时候,有没有将符号文件一并上传。效果如下


需要注意的点

在archive的时候,不要选bitCode,否则安装的二进制造成的crash无法符号化。原因使用bitCode上传给apple,bitCode只是一个中间码,需要根据不同的架构来决定最终生成的代码是什么?所以,如果使用bitCode上传的话,需要向apple下载dSYM文件。如下是使用bitcode发布时的流程图

参考地址

Understanding and Analyzing Application Crash Reports